home *** CD-ROM | disk | FTP | other *** search
/ Best of www.BestZips.com (Collector's Edition) / Best of WWW.BESTZIPS.COM Collector's Edition (JCSM Shareware) (JCS Marketing).ISO / egames__ / learn30.zip / ASK3.C < prev    next >
C/C++ Source or Header  |  1986-12-11  |  15KB  |  622 lines

  1. /* Copyright 1986 Academic Computing Center, University of Wisconsin - Madison
  2. **
  3. ** ASK to prompt the user to enter a character. Result is a value 0-255
  4. ** testable in a batch file with the "if errorlevel" construct.
  5. ** Compile with /ze/ox options.
  6. **
  7. ** Written by Peter Wu for the Faculty Support Center.
  8. */
  9.  
  10. #define LINT_ARGS
  11. #define SWITCHC '/'
  12. #define SPECIAL '\\'     /* the escape character */
  13. #define CONTROL '~'      /* control character prefix */
  14. #define XOPN '('         /* open quote for extended ascii e.g. ~(home) */
  15. #define XCLS ')'
  16.  
  17. #define acc(seg,off) ((long)(seg)<<16|(unsigned short)(off))
  18. #define peekb(seg,off) (*(unsigned char far *)acc(seg,off))
  19.  
  20. #include <stdio.h>
  21. #include <conio.h>
  22. #include <ctype.h>
  23. #include <string.h>
  24. #include <time.h>
  25. #include <signal.h>
  26. #include <stdlib.h>
  27. #include <dos.h>
  28. #include <memory.h>
  29.  
  30. char
  31.   *xasc[] = {  /* mnemonics for extended ascii */
  32.     "", "", "", "NULL?", "", "", "", "", "", "", "", "", "", "", "",
  33.     "S-Tab", "A-Q", "A-W", "A-E", "A-R", "A-T", "A-Y", "A-U", "A-I", "A-O",
  34.     "A-P", "", "", "", "", "A-A", "A-S", "A-D", "A-F", "A-G", "A-H", "A-J",
  35.     "A-K", "A-L", "", "", "", "", "", "A-Z", "A-X", "A-C", "A-V", "A-B",
  36.     "A-N", "A-M", "", "", "", "", "", "", "", "", "F1", "F2", "F3", "F4",
  37.     "F5", "F6", "F7", "F8", "F9", "F10", "", "", "Home", "Up", "PgUp", "",
  38.     "Left", "", "Right",
  39.     "", "End", "Down", "PgDn", "Ins", "Del", "S-F1", "S-F2", "S-F3",
  40.     "S-F4", "S-F5", "S-F6", "S-F7", "S-F8", "S-F9", "S-F10", "C-F1", "C-F2",
  41.     "C-F3", "C-F4", "C-F5", "C-F6", "C-F7", "C-F8", "C-F9", "C-F10", "A-F1",
  42.     "A-F2", "A-F3", "A-F4", "A-F5", "A-F6", "A-F7", "A-F8", "A-F9", "A-F10",
  43.     "C-PrtSc", "C-Left", "C-Right", "C-End", "C-PgDn", "C-Home", "A-1", "A-2",
  44.     "A-3",
  45.     "A-4", "A-5", "A-6", "A-7", "A-8", "A-9", "A-0", "A--", "A-=", "C-PgUp"
  46.   };
  47.  
  48. char *xi();
  49. char *xget();
  50. int ctrlc();
  51.  
  52. _setenvp(){}  /* diable code dealing with enironment variables */
  53.  
  54. _nullcheck()  /* disable null pointer checking */
  55. {
  56.   return 0;  /* this line is required */
  57. }
  58.  
  59. main()
  60. {
  61.   char
  62.     argvbuf[128],  /* string space for storing parameter */
  63.     *argv[65],       /* max # of parameters that fits on a line */
  64.     *prompt,       /* prompt string */
  65.     *option,       /* option string */
  66.     *tmp,
  67.     *nulls = "";
  68.  
  69.   int
  70.     argc,
  71.     optf,  /* 0 = no user options on cmd line; 1 = yes */
  72.     quiet,  /* 0 = enable error message; 1 = quiet */
  73.     flead,  /* flush type ahead flag 1=flush */
  74.     echo,
  75.     index,
  76.     timeout,  /* timeout value in seconds */
  77.     timeflag,  /* whether timeout options is set or not */
  78.     where,
  79.     i,
  80.     cases;  /* 0 = case non-sensitive; 1 = case sensitive */
  81.  
  82.   unsigned int
  83.     expect[200],  /* expected response string */
  84.     c;
  85.  
  86.   long
  87.     expire;  /* expire time = start time + timeout */
  88.  
  89.   getarg(&argc, argv, argvbuf);  /* my routine to do argc, argv */
  90.  
  91.   if (argc == 1) {  /* no argument */
  92.     cputs("ASK version 3.0 (Nov 21, 1986) pre-release\15\n\n");
  93.     cputs("Usage: ASK[/cefmqs] [prompt] [expected response]\15\n");
  94.     cputs("/c - case sensitive         /m### - timeout in ### minutes\15\n");
  95.     cputs("/e - no echo                /q - accept unexpected key\15\n");
  96.     cputs("/f - flush type-ahead       /s### - timeout in ### seconds\15\n");
  97.     cputs("E.G. (in batch file):\15\n");
  98.     cputs("  ASK \"Yes or No? \" yn\15\n");
  99.     cputs("  if errorlevel 2 goto NO\15\n");
  100.     exit(0);
  101.   }
  102.  
  103.   /* set default options */
  104.   quiet = 0;  /* not quiet; i.e. beeps on unexpected input */
  105.   cases = 0;  /* not case sensitive; i.e. a == A */
  106.   echo = 1;  /* echo input */
  107.   flead = 0;  /* no flush type-ahead, user can type ahead if he wants */
  108.   timeout = -1;  /* default timeout is immediate */
  109.   timeflag = 0;  /* timeout option not enabled */
  110.  
  111.   if (*argv[1] == SWITCHC) {  /* check for option string */
  112.  
  113.     option = argv[1] + 1;
  114.     optf = 1;  /* remember to shift prompt and expect */
  115.  
  116.     c = *option;
  117.     while (c) {
  118.  
  119.       option++;
  120.       switch (c) {
  121.  
  122.     case SWITCHC: case ' ':
  123.       break;  /* ignore extra switch char and space */
  124.  
  125.     case 'c': case 'C':
  126.       cases = 1;  /* set case sensitive */
  127.       break;
  128.  
  129.     case 'e': case 'E':  /* no echo option */
  130.       echo = 0;
  131.       break;
  132.  
  133.     case 'f': case 'F':  /* flush type ahead */
  134.       flead = 1;  /* the actual flushing is done below */
  135.       break;
  136.  
  137.     case 's':  case 'S':  /* timeout in seconds */
  138.       tmp = xi(option, &i);  /* read timeout value (in seconds) */
  139.       if (tmp > option) {  /* good, user supplied timeout value */
  140.         option = tmp;
  141.         if (timeflag) {  /* not the first timeout option */
  142.           timeout += i;  /* accumulate this timeout value */
  143.         } else {  /* first timeout option */
  144.           timeout = i;
  145.         }
  146.       }  /* if no timeout value is supplied, ignore it */
  147.       timeflag = 1;  /* enable timeout input */
  148.       break;
  149.  
  150.     case 'm':  case 'M':  /* timeout in minutes */
  151.       tmp = xi(option, &i);  /* read timeout value (in seconds) */
  152.       if (tmp > option) {  /* good, user supplied timeout value */
  153.         option = tmp;
  154.         if (timeflag) {  /* not the first timeout option */
  155.           timeout += i * 60;  /* convert minutes to seconds */
  156.         } else {  /* first timeout option */
  157.           timeout = i;
  158.         }
  159.       }
  160.       timeflag = 1;
  161.       break;
  162.  
  163.     case 'q': case 'Q':
  164.       quiet = 1;  /* disable error message for unexpected input */
  165.       break;
  166.  
  167.     default:
  168.       cputs("invalid option '"); putch(c); cputs("' ignored\15\n");
  169.       }
  170.       c = *option;
  171.     }  /* while */
  172.  
  173.   } else {  /* argv[1] is not an option string */
  174.  
  175.     optf = 0;
  176.  
  177.   }
  178.  
  179.   /* now figure out which is the prompt string, which is the expected
  180.   ** response string
  181.   */
  182.   if (argc-optf > 2) {    /* expected response string present */
  183.     if (!cases) {
  184.       strupr(argv[2+optf]);  /* convert to upper case if not case sensitive */
  185.     }
  186.     ex(argv[2+optf], expect);
  187.   } else {
  188.     expect[0] = 0;  /* no expected response string */
  189.   }
  190.  
  191.   if (argc-optf > 1) {    /* prompt string present */
  192.     prompt = argv[1+optf];
  193.   } else {
  194.     prompt = "? ";  /* default prompt string */
  195.   }
  196.  
  197.   if (!echo) {    /* if no echo then turn off the cursor */
  198.     cursor('s');  /* save cursor mode */
  199.     signal(SIGINT, ctrlc);  /* restore cursor if break key is hit */
  200.     cursor('h');  /* hide cursor */
  201.   }
  202.  
  203.   do {
  204.  
  205.     cputs(prompt);
  206.  
  207.     /* flush type-ahead if so requested */
  208.     if (flead) {
  209.       flush_ahead();
  210.     }
  211.  
  212.     /* process timeout if necessary */
  213.     if (timeflag) {
  214.  
  215.       /* if user selected timeout option without a timeout value, the default
  216.       ** value of -1 will be used. This means timeout immediately, it won't
  217.       ** even read type-ahead's in the keyboard buffers.
  218.       */
  219.       if (timeout < 0) {
  220.     cexit(255, echo);
  221.       }
  222.  
  223.       expire = time(NULL) + timeout;
  224.  
  225.       /* if user select timeout with value 0, the keyboard buffer will be
  226.       ** examined once meaning type-aheads will be read.
  227.       */
  228.       while (!kbhit()) {  /* while keyboard buffer is empty */
  229.     if (time(NULL) >= expire) {  /* timeout! */
  230.       cexit(255, echo);
  231.     }
  232.       }
  233.     }
  234.  
  235.     c = xgetc();  /* read a key from keyboard */
  236.  
  237.     if (echo) {
  238.       xputc(c);  /* echo extended character */
  239.       cputs("\15\n");
  240.     }
  241.  
  242.     if (!cases) {  /* not case sensitive, so convert it to UPPER */
  243.       if (c < 256) {  /* convert only normal ascii */
  244.     c = toupper(c);
  245.       }
  246.     }
  247.  
  248.     /* If no expected string is supplied, then return the character code
  249.     ** of the key (e.g. A returns 65) in errorlevel. If an extended ascii
  250.     ** key is pressed (e.g. [F0]) then this will return 0.
  251.     */
  252.     if (expect[0] == 0) {
  253.       cexit(c, echo);
  254.     }
  255.  
  256.     /* search for c in expected response string */
  257.     where = istrchr(expect, c);
  258.     if (where > -1) {  /* found! */
  259.       cexit(where + 1, echo);
  260.     }
  261.  
  262.     if (!quiet) {
  263.       if (!echo) {
  264.     cputs("\15\n");
  265.       }
  266.       cputs("\7unexpected key, please try again\15\n\n");
  267.     }
  268.  
  269.   } while (!quiet);
  270.  
  271.   cexit(0, echo);  /* means unexpected key press and quiet option set */
  272. }
  273.  
  274. xgetc()  /* get a character from keyboard. return extended ascii in msb */
  275. {
  276.   int c;
  277.  
  278.   c = getch();
  279.  
  280.   if (c == 0) {  /* did user typed an extended ascii? */
  281.     c = getch() << 8;  /* read the extended ascii */
  282.   }
  283.   return c;
  284. }
  285.  
  286. ctrlc()
  287. {
  288.   cursor('r');  /* restore cursor */
  289.   exit(0);
  290. }
  291.  
  292. flush_ahead()  /* flush type-ahead key strokes */
  293. {
  294.   char c;
  295.  
  296.   while (kbhit()) {  /* while there are keys in the key buffer */
  297.     c = getch();  /* read a key without echo */
  298.     if (c == 0) {  /* is it an extended ascii? */
  299.       (void) getch();  /* if so, read the extended portion too */
  300.     }
  301.   }
  302. }
  303.  
  304. ex(raw, cook)  /* translate ~x to extended ascii in cook */
  305. char *raw;
  306. int cook[];
  307. {
  308.   int i, sum;
  309.   char *tmp;
  310.  
  311.   i = 0;
  312.   while (*raw) {
  313.  
  314.     if (*raw != CONTROL) {  /* no need to translate */
  315.  
  316.       cook[i] = *raw;
  317.       i++;
  318.       raw++;
  319.  
  320.     } else {  /* could be an extended ascii spec */
  321.  
  322.       raw++;  /* examine char following CONTROL */
  323.       tmp = xget(raw, &sum);
  324.       if (tmp > raw) {    /* there's a number */
  325.     raw = tmp;
  326.  
  327.     /* now we have an extended ascii in sum, shift the byte (my way of
  328.     ** representing extended ascii).
  329.     */
  330.     cook[i] = sum << 8;
  331.     i++;
  332.  
  333.       } else {    /* ~ not followed by valid syntax, so don't treat it special */
  334.  
  335.     cook[i] = CONTROL;
  336.     cook[i+1] = *raw;
  337.     i += 2;
  338.     raw++;
  339.  
  340.       }
  341.     }
  342.   }
  343.   cook[i] = 0;    /* terminate integer string */
  344. }
  345.  
  346. istrchr(istr, c)  /* search for c in the integer string istr */
  347. int istr[];
  348. int c;
  349. {
  350.   int i;
  351.  
  352.   i = 0;
  353.   while (istr[i] != 0) {
  354.     if (c == istr[i]) {
  355.       return i;
  356.     }
  357.     i++;
  358.   }
  359.   return -1;  /* not found */
  360. }
  361.  
  362. cexit(ecode, echo)
  363. int ecode, echo;
  364. {
  365.   if (!echo) {
  366.     cursor('r');  /* restore cursor */
  367.   }
  368.   exit(ecode);
  369. }
  370.  
  371. /* routines to parse command line */
  372.  
  373. _setargv()
  374. {
  375. }
  376.  
  377. getarg(argcp, argv, argvbuf)
  378. int *argcp;
  379. char *argv[], *argvbuf;
  380. {
  381.   int c, p;
  382.   char *bp, quote;
  383.  
  384.   bp = argvbuf;
  385.   argv[0] = "?";
  386.   *argcp = 1;
  387.   p = 0x81;  /* where cmd line starts */
  388.  
  389.   do {
  390.  
  391.     argv[*argcp] = bp;
  392.  
  393.     do {
  394.       c = nextc(&p);
  395.     } while (c == ' ');  /* skip blank spaces */
  396.  
  397.     if (c == -1) {
  398.       return;
  399.     }
  400.  
  401.     if (c == '"') {
  402.       quote = '"';
  403.       c = nextc(&p);
  404.     } else {  /* no opening quote, so set quote to space */
  405.       quote = ' ';
  406.     }
  407.  
  408.     while ((c != -1) && (c != quote)) {
  409.       *bp = c;
  410.       bp++;
  411.       c = nextc(&p);
  412.     }
  413.  
  414.     *bp = '\0';  /* terminate this argument string */
  415.     bp++;
  416.     (*argcp)++;
  417.  
  418.   } while (c != -1);
  419. }
  420.  
  421. scan(pp,inc)  /* return character and increment pointer or -1 if no more */
  422. int inc, *pp;  /* 0 = no increment; 1 = post increment; -1 = pre increment */
  423. {
  424.   int c, last;
  425.  
  426.   last = 0x80 + peekb(_psp, 0x80);
  427.  
  428.   switch(inc) {
  429.  
  430.     case -1:  /* pre increment */
  431.       (*pp)++;
  432.       if (*pp > last) {
  433.     return -1;
  434.       } else {
  435.     c = peekb(_psp, *pp);
  436.     return c;
  437.       }
  438.  
  439.     case 0:
  440.       if (*pp > last) {
  441.     return -1;
  442.       } else {
  443.     c = peekb(_psp, *pp);
  444.     return c;
  445.       }
  446.  
  447.     case 1:
  448.       if (*pp > last) {
  449.     return -1;
  450.       } else {
  451.     c = peekb(_psp, *pp);
  452.     (*pp)++;
  453.     return c;
  454.       }
  455.   }
  456. }
  457.  
  458. nextc(pp)
  459. int *pp;  /* pointer to character pointer */
  460. {
  461.   int c;
  462.  
  463.   c = scan(pp, 1);
  464.  
  465.   if (c == -1) {
  466.     return -1;
  467.   } else if (c == SPECIAL) {
  468.     return spec(pp);  /* process special character */
  469.   } else if (c == CONTROL) {
  470.     c = scan(pp, 0);  /* peek at next character */
  471.     if (c >= 64 && c <= 95 || c >=97 && c <= 122) {
  472.       (void) scan(pp, 1);
  473.       return c & 31;  /* control character */
  474.     } else {  /* ~ not valid control seq, return everything including ~ */
  475.       return CONTROL;
  476.     }
  477.   } else {
  478.     return c;  /* return plain character */
  479.   }
  480. }
  481.  
  482. spec(pp)  /* process special character \ */
  483. int *pp;
  484. {
  485.   int c, sum;
  486.  
  487.   c = scan(pp, 1);
  488.   if (isdigit(c)) {  /* process "\027" or "\27" or "\0273"  and alike */
  489.     sum = c - '0';  /* convert to decimal value */
  490.     c = scan(pp, 0);
  491.     if (isdigit(c)) {
  492.       sum = 10 * sum + c - '0';
  493.       c = scan(pp, -1);  /* last digit */
  494.       if (isdigit(c)) {
  495.     sum = 10 * sum + c - '0';
  496.     scan(pp, 1);  /* advance pass this digit */
  497.       }
  498.     }
  499.     return sum;
  500.   }
  501.  
  502.   /* not a digit following \ */
  503.   c = tolower(c);
  504.  
  505.   switch(c) {
  506.     case 'e': return '\33';  /* escape */
  507.     default: return c | 256;  /* quotes, special, control, .. */
  508.   }
  509. }
  510.  
  511. char * xi(s,v)    /* extract integer */
  512. char *s;
  513. int *v;
  514. {
  515.   int sum;
  516.  
  517.   if (isdigit(*s)) {
  518.     sum = *s - '0';  /* convert to decimal value */
  519.     s++;
  520.     if (isdigit(*s)) {
  521.       sum = 10 * sum + *s - '0';
  522.       s++;
  523.       if (isdigit(*s)) {
  524.     sum = 10 * sum + *s - '0';
  525.     s++;  /* advance pass this digit */
  526.       }
  527.     }
  528.   }
  529.   *v = sum;
  530.   return s;
  531. }
  532.  
  533. cursor(mode)
  534. char mode;
  535. {
  536.   static int oldc;  /* save old cursor setting */
  537.   union REGS inregs, outregs;
  538.  
  539.   switch (mode) {
  540.  
  541.     case 's':  /* save cursor mode */
  542.       inregs.h.ah = 3;
  543.       inregs.h.bh = 1;
  544.       int86(0x10, &inregs, &outregs);
  545.       oldc = outregs.x.cx;  /* save old cursor */
  546.       break;
  547.  
  548.     case 'h':  /* hide cursor */
  549.       inregs.h.ah = 1;
  550.       inregs.h.ch = 32;
  551.       inregs.h.cl = 0;
  552.       int86(0x10, &inregs, &outregs);  /* turn off cursor */
  553.       break;
  554.  
  555.     case 'r':  /* restore cursor */
  556.       inregs.h.ah = 1;
  557.       inregs.x.cx = oldc;
  558.       int86(0x10, &inregs, &outregs);  /* restore cursor */
  559.   }
  560. }
  561.  
  562. /* extract extended ASCII entered in this form:
  563. **  ~mne     where mne is the mnemonic, like (home), (pgdn), (up), (f1)
  564. */
  565. char *xget(raw, sum)
  566. char *raw;
  567. int *sum;
  568. {
  569.   char *cls,  /* point to XCLS (close bracket) */
  570.        mne[9],
  571.        tmp[9];
  572.   int cnt, i;
  573.  
  574.   if (*raw == XOPN) {  /* scan [mne] format */
  575.     cls = strchr(raw, XCLS);
  576.     if (cls != NULL) {
  577.       /* found open and close bracket, now look at the string inside
  578.       ** to see if it matches an extended ascii' mnemonic
  579.       */
  580.       cnt = cls - raw - 1;  /* length of string between brackets */
  581.       if (cnt < 9) {  /* mnemonic can only be this long */
  582.  
  583.     memcpy(mne, raw+1, cnt);  /* make a duplicate for processing */
  584.     mne[cnt] = '\0';  /* terminate the string */
  585.     strupr(mne);  /* convert mnemonic to upper case */
  586.  
  587.     for (i=15; i < 133; i++) {  /* range of extended ascii */
  588.       if (*xasc[i] != '\0') {  /* if not a null string */
  589.         strcpy(tmp, xasc[i]);  /* make a copy */
  590.         strupr(tmp);  /* convert this to upper case also */
  591.         if (strcmp(mne,tmp) == 0) {  /* match!!!! */
  592.           *sum = i;  /* return extended ascii */
  593.           return cls+1;
  594.         }
  595.       }
  596.     }
  597.       }
  598.     }
  599.  
  600.     /* invalid format; don't translate anything */
  601.     return raw;
  602.  
  603.   } else {
  604.     return raw;
  605.   }
  606. }
  607.  
  608. xputc(c)
  609. unsigned int c;
  610. {
  611.   if (c < 256) {  /* normal ascii */
  612.     if (c > 31) {  /* printable ascii */
  613.       putch(c);
  614.     } else if (c != 13) {  /* don't echo Enter */
  615.       putch('^'); putch(c | 64);  /* print control character nicely */
  616.     }
  617.   } else {  /* extended ascii */
  618.     putch(XOPN); cputs(xasc[c >> 8]); putch(XCLS);
  619.   }
  620. }
  621.  
  622.